package com.bingxue.edu.framework.job;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import com.bingxue.edu.common.enums.JobInitStrategyEnum;
import com.bingxue.edu.framework.common.util.SpringContextHolder;
import com.bingxue.edu.framework.exception.SystemException;
import com.bingxue.edu.management.system.model.entity.ScheduleJob;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author panbo
 * @since 2024/8/2
 */
@SuppressWarnings("unchecked")
@Slf4j
@Service
@RequiredArgsConstructor
public class QuartzSchedulerService {

    private final Scheduler scheduler;

    /**
     * 任务缓存
     */
    public static final List<Map<String, Object>> CACHE_JOB = new ArrayList<>();

    @PostConstruct
    public void startScheduler() {
        try {
            scheduler.start();
        } catch (SchedulerException e) {
            log.error("定时任务scheduler初始化异常，请检查！", e);
        }
    }

    /**
     * 添加job
     */
    public void addJob(ScheduleJob job) {
        TriggerKey triggerKey = TriggerKey.triggerKey(job.getId());
        // 构建参数
        JobDetail jobDetail = JobBuilder.newJob(getJobClass(job.getJobKey())).withIdentity(job.getId()).build();
        if (StrUtil.isNotEmpty(job.getParamJson())) {
            HashMap<String, Object> jsonData = JSONUtil.parseObj(job.getParamJson()).toBean(HashMap.class);
            jobDetail.getJobDataMap().putAll(jsonData);
        }
        try {
            // 表达式调度构建器
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron());
            // 设置定时任务初始化策略
            if (JobInitStrategyEnum.FIRE_AND_PROCEED.equals(job.getInitStrategy())) {
                cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
            } else if (JobInitStrategyEnum.IGNORE_MISFIRES.equals(job.getInitStrategy())) {
                cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
            }
            // 定时任务
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
            log.info("加载定时任务: {}", job.getJobKey());
        } catch (Exception e) {
            throw new SystemException("定时任务添加异常", e);
        }
    }

    /**
     * 添加一个立即执行一次的job
     *
     * @param job
     */
    public void addJobExecuteOnce(ScheduleJob job) {
        TriggerKey triggerKey = TriggerKey.triggerKey(job.getId());
        // 构建参数
        JobDetail jobDetail = JobBuilder.newJob(getJobClass(job.getJobKey())).withIdentity(job.getId()).build();
        if (StrUtil.isNotEmpty(job.getParamJson())) {
            HashMap<String, Object> jsonData = JSONUtil.parseObj(job.getParamJson()).toBean(HashMap.class);
            jobDetail.getJobDataMap().putAll(jsonData);
        }
        try {
            // 立即执行,且只执行一次
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0)).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            throw new SystemException("定时任务添加异常", e);
        }
    }

    /**
     * 立即执行job
     */
    public void runJob(String jobId) {
        try {
            scheduler.triggerJob(JobKey.jobKey(jobId));
        } catch (SchedulerException e) {
            log.error("运行job异常", e);
        }
    }

    /**
     * 暂停job
     */
    public void pauseJob(String jobId) {
        try {
            scheduler.pauseJob(JobKey.jobKey(jobId));
        } catch (Exception e) {
            log.error("暂停job异常", e);
        }
    }

    /**
     * 恢复job
     */
    public void resumeJob(String jobId) {
        try {
            scheduler.resumeJob(JobKey.jobKey(jobId));
        } catch (SchedulerException e) {
            log.error("恢复job异常", e);
        }
    }

    /**
     * 删除job
     */
    public void deleteJob(String jobId) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobId);
        try {
            scheduler.pauseTrigger(triggerKey);
            scheduler.unscheduleJob(triggerKey);
            scheduler.deleteJob(JobKey.jobKey(jobId));
        } catch (Exception e) {
            log.error("删除job异常", e);
        }
    }

    /**
     * 更新一个job的cron表达式
     */
    public void updateJobCron(String jobId, String cron) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobId);
        try {
            Trigger trigger = scheduler.getTrigger(triggerKey);
            CronTrigger cronTrigger = (CronTrigger) trigger;
            if (!cronTrigger.getCronExpression().equals(cron)) {
                cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(cron)).startNow().build();
                scheduler.rescheduleJob(triggerKey, cronTrigger);
            }
        } catch (Exception e) {
            log.error("更新job的cron定时表达式异常", e);
        }
    }

    /**
     * 判断是否存在job
     */
    public boolean existJob(String jobId) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobId.toString());
        try {
            return scheduler.checkExists(triggerKey);
        } catch (SchedulerException e) {
            log.error("判断是否存在job异常", e);
        }
        return false;
    }

    /**
     * 获取当前系统中的所有定时任务
     */
    public List<Map<String, Object>> loadAllJobs() {
        if(!CACHE_JOB.isEmpty()) {
            return CACHE_JOB;
        }
        // 获取所有被DynamicJob注解的job
        List<Object> annoJobList = SpringContextHolder.getBeanListByAnnotation(DynamicJob.class);
        List<Map<String, Object>> result = loadJobs(annoJobList);
        CACHE_JOB.addAll(result);
        return CACHE_JOB;
    }

    /**
     * 获取调度器重的所有任务列表
     *
     * @return
     */
    public List<Map<String, Object>> loadJobsInScheduler() {
        List<Map<String, Object>> jobList = null;
        try {
            Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.anyJobGroup());
            jobList = new ArrayList<>();
            for (JobKey jobKey : jobKeys) {
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("jobName", jobKey.getName());
                    map.put("jobGroupName", jobKey.getGroup());
                    map.put("description", trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    map.put("jobStatus", triggerState.name());
                    if (trigger instanceof CronTrigger cronTrigger) {
                        String cronExpression = cronTrigger.getCronExpression();
                        map.put("jobTime", cronExpression);
                    }
                    jobList.add(map);
                }
            }
        } catch (SchedulerException e) {
            log.error("加载全部Job异常", e);
        }
        return jobList;
    }

    /**
     * 加载job定义
     *
     * @param annoJobList 注解任务
     * @return job定义
     */
    private List<Map<String, Object>> loadJobs(List<Object> annoJobList) {
        List<Map<String, Object>> result = new ArrayList<>();
        for (Object job : annoJobList) {
            if (!(job instanceof Job)) {
                log.warn("无效的job任务: {}", job.getClass());
                continue;
            }
            Map<String, Object> temp = new HashMap<>(8);
            Class<?> targetClass = AopProxyUtils.ultimateTargetClass(job);
            DynamicJob annotation = targetClass.getAnnotation(DynamicJob.class);
            temp.put("jobKey", targetClass.getSimpleName());
            temp.put("jobCron", annotation.cron());
            temp.put("jobName", annotation.name());
            temp.put("jobClass", targetClass);
            Class<?> paramClass = annotation.paramClass();
            String paramJsonExample = null;
            try {
                Object paramInstance = paramClass.getDeclaredConstructor().newInstance();
                paramJsonExample = JSONUtil.toJsonPrettyStr(JSONUtil.parse(paramInstance, JSONConfig.create().setIgnoreNullValue(false)));
            } catch (Exception e) {
                log.error("job任务：{}, Scheduled#paramClass参数任务无效", job.getClass());
            }
            temp.put("paramJsonExample", paramJsonExample);
            result.add(temp);
        }
        return result;
    }

    /**
     * 获取任务执行类
     *
     * @param jobKey job key
     * @return jobClass 任务执行类
     */
    public Class<? extends Job> getJobClass(String jobKey) {
        try {
            Class<? extends Job> jobClass = loadAllJobs().stream()
                    .filter(e -> String.valueOf(e.get("jobKey")).equals(jobKey))
                    .map(e -> (Class<? extends Job>) e.get("jobClass"))
                    .findAny()
                    .orElse(null);
            if (jobClass == null) {
                throw new SystemException(String.format("找不到jobKey为%s的定时任务", jobKey));
            }
            return jobClass;
        } catch (Exception e) {
            log.error("定时任务加载失败", e);
            throw new SystemException(String.format("加载定时任务%s失败", jobKey));
        }
    }
}
